﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Annotations;
using System.Windows.Annotations.Storage;
using System.IO;
using System.Windows.Input;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Documents;

namespace OrganizeYourSearch
{
    public class DocContainerClosableTabItem:CloseableTabItem
    {
        #region Private Fields
        // For Annotations
        AnnotationService _annServ = null;
        AnnotationStore _annStore = null;
        MemoryStream _annotationBuffer = null;
        FileStream _annotationFileStream = null;
        ContextMenu fdpvContextMenu = null;
        enum AnnotationType : int { Bookmark, Comments, Highlight, Important, Question, Tag };
        AnnotationType _annotType;
        List<string> TypeAnnotationList = new List<string>()
        {
            "Bookmark",
            "Comments",
            "Highlight",
            "Important",
            "Question"
        };

        //The UI components that need to be accessible
        VerticalPlacementTabControl _vertTabCtrl=null;
        FlowDocumentPageViewer FDPV = null;
        
        // filename open
        string _fileName = string.Empty;
        string _annfileName = string.Empty;
        #endregion Private Fields

        #region Constructor
        public DocContainerClosableTabItem():base()
        {
            
        }
        private Color ColorFromHexValue(string hexColor)
        {
            Color c = Color.FromArgb(
                        Convert.ToByte(hexColor.Substring(1, 2), 16),
                        Convert.ToByte(hexColor.Substring(3, 2), 16),
                        Convert.ToByte(hexColor.Substring(5, 2), 16),
                        Convert.ToByte(hexColor.Substring(7, 2), 16)
                    );
            return c;
        }

        public DocContainerClosableTabItem(FrameworkElement documentViewer, string fileName)
        {
            try{
                Style mystyle = new Style();

                DockPanel _mainDock = new DockPanel();
                _mainDock.Background = new SolidColorBrush(ColorFromHexValue("#FFB1B1DA"));
                _vertTabCtrl = new VerticalPlacementTabControl();
                _vertTabCtrl.Background = new SolidColorBrush(Colors.White);
                FDPV=documentViewer as FlowDocumentPageViewer;
                
                _mainDock.Children.Add(_vertTabCtrl);
                
                _fileName = fileName;

                _mainDock.Children.Add(FDPV);
                this.Content = _mainDock;
                
                FDPV.Background = new SolidColorBrush(Colors.White);
                AddCommandHandlers(this);
                this.AddContextMenuItemsToFDPV();
                FDPV.ContextMenuOpening += new ContextMenuEventHandler(FDPV_ContextMenuOpening);


                //Getting Annotation filestream filename from _fileName
                _annfileName = _fileName.Remove(_fileName.LastIndexOf("\\"));
                _annfileName = string.Concat(_annfileName, "\\annotations.xml");
                
                // Enable Annotations
                _annotationBuffer = new MemoryStream();
                _annotationFileStream = new FileStream(_annfileName, FileMode.OpenOrCreate, FileAccess.ReadWrite);

                // if annotationfilestream has annotation data already stored, copy it to memory stream
                if (_annotationFileStream.Length > 5)
                {
                    Byte[] bytes = new Byte[_annotationFileStream.Length];
                    //read the file content to bytes
                    _annotationFileStream.Read(bytes, 0, (int)_annotationFileStream.Length);
                    //write the contents of bytes to memorystream
                    _annotationBuffer.Write(bytes, 0, (int)_annotationFileStream.Length);
                }
                _annotationFileStream.Close();
                _annotationBuffer.Seek(0, 0);
                _annotationBuffer.Flush();
                _annStore = new XmlStreamStore(_annotationBuffer);
                _annServ = new AnnotationService(FDPV);
                _annStore.StoreContentChanged +=
                    new StoreContentChangedEventHandler(_annStore_StoreContentChanged);
                _annServ.Enable(_annStore);

                //Creating respective treeViewItem or ListBoxItem for each annotation 
                IList<Annotation> annotations = _annServ.Store.GetAnnotations();
                if (annotations.Count > 0)
                {
                    foreach (Annotation ann in annotations)
                    {
                        string type;
                        if (ann.Cargos.Count > 1)
                        {
                            type = ann.Cargos[2].Name;
                            switch (type)
                            {
                                case "Bookmark":
                                    AddBookmarkToTree(_vertTabCtrl.tviewBookmark, ann, "..\\Images\\bookmark.ico");
                                    break;
                                case "Comments":
                                    AddAnnotationToRespList(_vertTabCtrl.lbComment, ann, "..\\Images\\comment.ico");
                                    break;
                                case "Highlight":
                                    AddAnnotationToRespList(_vertTabCtrl.lbHighlight, ann, "..\\Images\\bookmark.ico");
                                     break;
                                case "Important":
                                    AddAnnotationToRespList(_vertTabCtrl.lbImportant, ann, "..\\Images\\important.ico");
                                    break;
                                case "Question":
                                     AddAnnotationToRespList(_vertTabCtrl.lbQuestion, ann, "..\\Images\\zoom out.ico");
                                    break;
                                case "Tag":
                                    throw (new NotImplementedException());
                                    
                                default:
                                    throw (new NotImplementedException());
                            }//end switch
                        }//end if
                    }// end foreach
                }// end if
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }



        void FDPV_ContextMenuOpening(object sender, ContextMenuEventArgs e)
        {
            FDPV.ContextMenu = fdpvContextMenu;
        }

        private void AddContextMenuItemsToFDPV()
        {
            MenuItem ItemBookmark= new MenuItem();
            ItemBookmark.Header = "Add Bookmark";
            ItemBookmark.Command = AddBookmark;
            MenuItem ItemComment = new MenuItem();
            ItemComment.Header = "Add Comment";
            ItemComment.Command = AddComment;
            MenuItem ItemImportant = new MenuItem();
            ItemImportant.Header = "Add As Important";
            ItemImportant.Command = AddImportant;
            MenuItem ItemQuestion = new MenuItem();
            ItemQuestion.Header = "Add As Question";
            ItemQuestion.Command = AddQuestion;
            MenuItem ItemTag = new MenuItem();
            ItemTag.Header = "Add As Tag";
            ItemTag.Command = AddTag;
            fdpvContextMenu = new ContextMenu();
            fdpvContextMenu.Items.Add(ItemBookmark);
            fdpvContextMenu.Items.Add(ItemComment);
            fdpvContextMenu.Items.Add(ItemImportant);
            fdpvContextMenu.Items.Add(ItemQuestion);
            fdpvContextMenu.Items.Add(new Separator());
            fdpvContextMenu.Items.Add(ItemTag);
            
        }

        #endregion COnsturctor

        #region Command Declaration and Binding
/*        private static RoutedCommand DeclareCommand(ref RoutedCommand command,
                                              string commandDebugName)
        {   command = new RoutedCommand(commandDebugName,
                                            typeof(DocContainerClosableTabItem));
            return command;
        }
*/
        private static RoutedCommand _AddBookmark;
        public static RoutedCommand AddBookmark
        {
            get
            {
                return _AddBookmark;//DeclareCommand(ref _AddBookmark, "AddBookmark");
            } 
        }

        private static RoutedCommand _AddComment;
        public static RoutedCommand AddComment
        {
            get
            {
                return _AddComment;// DeclareCommand(ref _AddComment, "AddComment"); 
            } 
        }

        private static RoutedCommand _AddQuestion;
        public static RoutedCommand AddQuestion
        {
            get
            {
                return _AddQuestion;// DeclareCommand(ref _AddQuestion, "AddQuestion"); 
            }
        }

        private static RoutedCommand _AddImportant;
        public static RoutedCommand AddImportant
        {
            get
            {
                return _AddImportant;// DeclareCommand(ref _AddImportant, "AddImportant"); 
            }
        }
        private static RoutedCommand _AddTag;
        public static RoutedCommand AddTag
        {
            get
            {
                return _AddTag;// DeclareCommand(ref _AddTag, "AddTag"); 
            }
        }
        private void AddCommandHandlers(FrameworkElement uiScope)
        {
            // Add Command Handlers
            CommandBindingCollection commandBindings = uiScope.CommandBindings;

            _AddBookmark = new RoutedCommand();
            _AddComment = new RoutedCommand("AddComment",typeof(DocContainerClosableTabItem));
            _AddImportant = new RoutedCommand("AddImportant", typeof(DocContainerClosableTabItem));
            _AddQuestion = new RoutedCommand("AddQuestion", typeof(DocContainerClosableTabItem));
            _AddTag = new RoutedCommand("AddTag", typeof(DocContainerClosableTabItem));
            //For the rest of the commands (ie commands declared above), use this simple method to add the bindings

            commandBindings.Add(
                new CommandBinding(DocContainerClosableTabItem.AddBookmark,
                            new ExecutedRoutedEventHandler(AddBookmark_Executed),
                            new CanExecuteRoutedEventHandler(AddAnnots_CanExecute)));

            commandBindings.Add(
                new CommandBinding(DocContainerClosableTabItem.AddComment,
                            new ExecutedRoutedEventHandler(AddComment_Executed),
                            new CanExecuteRoutedEventHandler(AddAnnots_CanExecute)));

            commandBindings.Add(
                new CommandBinding(DocContainerClosableTabItem.AddImportant,
                            new ExecutedRoutedEventHandler(AddImportant_Executed),
                            new CanExecuteRoutedEventHandler(AddAnnots_CanExecute)));

            commandBindings.Add(
                new CommandBinding(DocContainerClosableTabItem.AddQuestion,
                            new ExecutedRoutedEventHandler(AddQuestion_Executed),
                            new CanExecuteRoutedEventHandler(AddAnnots_CanExecute)));

            commandBindings.Add(
                new CommandBinding(DocContainerClosableTabItem.AddTag,
                            new ExecutedRoutedEventHandler(AddTag_Executed),
                            new CanExecuteRoutedEventHandler(AddAnnots_CanExecute)));
  

        }// end:AddCommandHandlers()
        #endregion Command Declaration and Binding

        #region CommandExecution

        void AddAnnots_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (FDPV.Selection.IsEmpty)
            {
                e.CanExecute = false;
            }
            else
            {
                e.CanExecute = true;
            }
            e.Handled = true;
        }

        void AddBookmark_Executed(object sender, ExecutedRoutedEventArgs  e)
        {
            try
            {
                
                Color col = (Color)ColorConverter.ConvertFromString("#4AFFFF00");
                Brush myBrush = new SolidColorBrush(col);
                string userName = System.Windows.Forms.SystemInformation.UserName;
               
                //Setting the Annotation Type
                _annotType = AnnotationType.Bookmark;

                AnnotationHelper.CreateHighlightForSelection(_annServ, userName, myBrush);
                
            }
            catch (InvalidOperationException ex)
            {
                throw ex;
            }
            finally
            {
                e.Handled = true;
            }
            //AddBookmarkOrComment(BookmarkList);
        }

        void AddComment_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            try
            {
                string userName = System.Windows.Forms.SystemInformation.UserName;
                //Setting the AnnotationType
                _annotType = AnnotationType.Comments;
                AnnotationHelper.CreateTextStickyNoteForSelection(_annServ, userName);
            }
            catch (InvalidOperationException ex)
            {
                throw ex;
            }
            finally
            {
                e.Handled = true;
            }

        }
        void AddImportant_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            try
            {
                Color col = (Color)ColorConverter.ConvertFromString("#4A00FF00");
                Brush myBrush = new SolidColorBrush(col);
                string userName = System.Windows.Forms.SystemInformation.UserName;
                //Setting the Annotation Type
                _annotType = AnnotationType.Important;

                AnnotationHelper.CreateHighlightForSelection(_annServ, userName, myBrush);
            }
            catch (InvalidOperationException ex)
            {
                throw ex;
            }
            finally
            {
                e.Handled = true;
            }

        }
        void AddQuestion_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            try
            {
                Color col = (Color)ColorConverter.ConvertFromString("#4AFF0000");
                Brush myBrush = new SolidColorBrush(col);
                string userName = System.Windows.Forms.SystemInformation.UserName;
                //Setting the Annotation Type
                _annotType = AnnotationType.Question;

                AnnotationHelper.CreateHighlightForSelection(_annServ, userName, myBrush);
            }
            catch (InvalidOperationException ex)
            {
                throw ex;
            }
            finally
            {
                e.Handled = true;
            }

        }
        void AddTag_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            try
            {
                string selectedText = FDPV.Selection.Text;
                
                string docTitle = _fileName.Substring(_fileName.LastIndexOf("\\")+1, (_fileName.LastIndexOf(".") - _fileName.LastIndexOf("\\")));
                docTitle = docTitle.ToUpper();
                int start = 0;
                int end = selectedText.Length;

                WinInsertTag tagWindow = new WinInsertTag(selectedText, docTitle, _fileName, start, end);
                tagWindow.Show();
               
            }
            catch (InvalidOperationException ex)
            {
                throw ex;
            }
            finally
            {
                e.Handled = true;
            }

        }
# endregion
        // ------------------ _annStore_StoreContentChanged -------------------
        //-------Called when delete or insert an annotation -------------------
        void _annStore_StoreContentChanged(object sender, StoreContentChangedEventArgs e)
        {


            if (e.Action == StoreContentAction.Deleted) return;

            Annotation ann = e.Annotation;
            ann.Cargos.Add(new AnnotationResource(FDPV.MasterPageNumber.ToString()));
            switch (_annotType)
            {
                case AnnotationType.Bookmark:
                    ann.Cargos.Add(new AnnotationResource("Bookmark"));
                    AddBookmarkToTree(_vertTabCtrl.tviewBookmark, ann, "..\\Images\\bookmark.ico");
                    break;

                case AnnotationType.Comments:
                    ann.Cargos.Add(new AnnotationResource("TextStickyNote")); //You can use any name here. We are not using this.
                    ann.Cargos.Add(new AnnotationResource("Comments"));
                    AddAnnotationToRespList(_vertTabCtrl.lbComment, ann, "..\\Images\\comment.ico");
                    break;
                    
                case AnnotationType.Highlight:
                    ann.Cargos.Add(new AnnotationResource("Highlight"));
                    AddAnnotationToRespList(_vertTabCtrl.lbHighlight, ann, "..\\Images\\bookmark.ico");
                    break;

                case AnnotationType.Important:
                    ann.Cargos.Add(new AnnotationResource("Important"));
                    AddAnnotationToRespList(_vertTabCtrl.lbImportant, ann, "..\\Images\\important.ico");
                    break;

                case AnnotationType.Question:
                    ann.Cargos.Add(new AnnotationResource("Question"));
                    AddAnnotationToRespList(_vertTabCtrl.lbQuestion, ann, "..\\Images\\zoom out.ico");
                    break;
                    
                default:
                    throw new NotImplementedException();

            }
            FlushAnnotationsToFile();
       }

        private void AddAnnotationToRespList(ListBox collection, Annotation ann,string imagePath)
        {
            string ToolTipString = FindAnchoredTextFromAnnotation(ann);
            ListItemWithIcons item = new ListItemWithIcons(imagePath, ToolTipString,ann);
            collection.Items.Add(item);
            item.Tag = ann;
            item.MouseUp += new MouseButtonEventHandler(item_MouseUp);
            item.DeleteLink.Click += new RoutedEventHandler(DeleteLink_Click);
        }

        // ----------------------- AddBookmarkOrComment -----------------------
        private void AddBookmarkToTree(TreeView collection, Annotation ann,string imagePath)
        {
            string ToolTipString = FindAnchoredTextFromAnnotation(ann);
            TreeItemWithIcons item=new TreeItemWithIcons(imagePath,ToolTipString,ann);
            item.Tag = ann;
            collection.Items.Add(item);
            item.MouseUp += new MouseButtonEventHandler(item_MouseUp);
            item.DeleteLink.Click += new RoutedEventHandler(DeleteLink_Click);
            
        }

        //Works for all Annotation List Items ie for TreeItemWithIcons and for ListItemWithIcons too.
        void DeleteLink_Click(object sender, RoutedEventArgs e)
        {
            MenuItem thisMenu = sender as MenuItem;
            ContextMenu parentMenu = thisMenu.Parent as ContextMenu;
            FrameworkElement parent =
                parentMenu.PlacementTarget as FrameworkElement;
            FrameworkElement child = new FrameworkElement();
            while (!(parent is TreeView) && !(parent is ListBox))
            {
                child = parent;
                parent = parent.Parent as FrameworkElement;
            }
            Annotation ann = child.Tag as Annotation;
            _annStore.DeleteAnnotation(ann.Id);
            _annStore.Flush();
            if (parent.GetType() == typeof(TreeView))
            {
                TreeView tree = parent as TreeView;
                tree.Items.Remove(child as TreeItemWithIcons);
            }
            else if (parent.GetType() == typeof(ListBox))
            {
                ListBox list = parent as ListBox;
                list.Items.Remove(child as ListItemWithIcons);
            }
            else
                throw new NotImplementedException();            
        }

       /// <summary>
       /// When the TreeItemWithIcons or ListItemWithIcons is clicked, find the corresponding annotation in the document
       /// </summary>
       /// <param name="sender"></param>
       /// <param name="e"></param>
        void item_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Annotation ann = null;
            if (sender is TreeItemWithIcons)
                ann = ((TreeItemWithIcons)sender).Tag as Annotation;
            else if (sender is ListItemWithIcons)
                ann = ((ListItemWithIcons)sender).Tag as Annotation;
            if (ann == null) return;

            ContentLocator cloc =
                ann.Anchors[0].ContentLocators[0] as ContentLocator;
            if (cloc == null) return;
            if (cloc.Parts.Count < 2) return;

            ContentLocatorPart cPart = cloc.Parts[1];
            if (cPart == null) return;
            if (cPart.NameValuePairs["Segment0"] != null)
            {
                string[] charPos = cPart.NameValuePairs["Segment0"].Split(',');
                FlowDocument fd = FDPV.Document as FlowDocument;

                TextPointer tp = fd.ContentStart.GetPositionAtOffset(
                                 int.Parse(charPos[0]), LogicalDirection.Forward);
                if (tp == null) return;

                TextPointer tp2 = fd.ContentStart.GetPositionAtOffset(
                                    //    int.Parse(charPos[0]) + 
                                        int.Parse(charPos[1]),
                                        LogicalDirection.Forward);

                TextRange range = new TextRange(tp, tp2); 
                string newstring = range.Text;
                //newstring = "<run>" + newstring + "Blaaaa</run>";
                //tp=fd.ContentStart.GetPositionAtOffset(int.Parse(charPos[0]+5)); // adding for <run>
                //range.Text = newstring;

                FrameworkContentElement fce = tp.Parent as FrameworkContentElement;
                if (fce == null) return;

                fce.BringIntoView();
            }
        }


        private string FindAnchoredTextFromAnnotation(Annotation ann)
        {
              // Get the first content locator in the anchor property. There is only one anchor and only one content locator by default
                ContentLocator cloc =
                    ann.Anchors[0].ContentLocators[0] as ContentLocator;
                // By default, conent locator has 2 parts, dataID and CharacterRange. So, if this requirement is not met, don't proceed
                if (cloc == null) return "";
                if (cloc.Parts.Count < 2) return "";
                // Now, we are looking for the CharacterRange Part of the Content Locator.
                ContentLocatorPart cPart = cloc.Parts[1];
                if (cPart == null) return "";
                // CharacterRange property has one Count Property that has the value "N", a number which probably indicates the number
                // of segments. This value 'N'determines the num of segments of the character range. For a value "N" of Count Property,
                // there are SegmentXX segments where XX ranges from 0 to N-1. So, in total there are N segments.
                // We generally have only one segment. And we assume that we don't have more segments. So, we only have Segment0 as our 
                // segment
                // Each Name=SegmentXX has value="offset,range" as its value. SO, now using Segment0 and its value as name-value pair,

                if (cPart.NameValuePairs["Segment0"] != null)
                {
                    // Now, we extract the value parts of the 'Segemnt0' name and split it into an array of string containing:
                    //charpos[0]=offset  charpos[1]=range
                    string[] charPos = cPart.NameValuePairs["Segment0"].Split(',');
                    //MessageBox.Show(charPos[0] + "  " + charPos[1]);
                    FlowDocument fd = FDPV.Document as FlowDocument;
                    //mark the textpointer at the offset position. int.parse(string) converts string into integer.
                    TextPointer tp = fd.ContentStart.GetPositionAtOffset(
                        int.Parse(charPos[0]), LogicalDirection.Forward);

                    if (tp == null) return "";
                    string MarkText = "";
                    string RequiredTextToDisplay = "";
                    //Test if the pointer actually points to the text or not
                     while (tp.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text)
                    {
                        tp = tp.GetNextContextPosition(LogicalDirection.Forward);
                        
                    }
                     MarkText += tp.GetTextInRun(LogicalDirection.Forward);
                    int numchars=Convert.ToInt32(charPos[1])-Convert.ToInt32(charPos[0]);
                    if (MarkText.Length < numchars)
                        numchars = MarkText.Length;
                    //Put upto 150 chars max of the run into the spText of the button.
                    RequiredTextToDisplay = MarkText.Substring(0,
                        ( numchars> 150) ? 150 : numchars);

                    return RequiredTextToDisplay;
                }
                return "";
               
        }
        // ------------------------- DeleteMark_Click -------------------------
        void DeleteMark_Click(object sender, RoutedEventArgs e)
        {


        }


        public void FlushAnnotationsToFile()
        {
            _annServ.Store.Flush();
            _annotationFileStream = new FileStream(_annfileName, FileMode.Create, FileAccess.Write);
            _annotationBuffer.WriteTo(_annotationFileStream);
            _annotationFileStream.Close();
        }
    }
}
